home *** CD-ROM | disk | FTP | other *** search
/ AOL File Library: 2,801 to 2,900 / aol-file-protocol-4400-2801-to-2900.zip / AOLDLs / C++ Files Library / CWASTE C++ text editing routi / CWASTE folder.sit / CWASTE folder / WASTE1.c < prev    next >
Text File  |  1994-07-20  |  27KB  |  970 lines

  1. // { WASTE PROJECT: }
  2. // { Unit One: Low-level routines; Drawing }
  3.  
  4. // { Copyright ⌐ 1993-1994 Marco Piovanelli }
  5. // { All Rights Reserved }
  6. // C conversion by Dan Crevier
  7.  
  8. #include "WASTEIntf.h"
  9. #include <Palettes.h>
  10. #include <QDOffscreen.h>
  11.  
  12. long _WEOffsetToLine (long offset, WEHandle hWE)
  13. {
  14.     //{ given a byte offset into the text, find the corresponding line index }
  15.  
  16.     WEPtr pWE;
  17.     LineArrayPtr pLines;
  18.     long minIndex, maxIndex, index;
  19.     
  20.     pWE = *hWE;
  21.  
  22.     //{ get a pointer to the line array }
  23.     pLines = *pWE->hLines;
  24.  
  25.     //{ do a fast binary search through the style run array }
  26.     minIndex = 0;
  27.     maxIndex = pWE->nLines;
  28.  
  29.     while (minIndex < maxIndex)
  30.     {
  31.         index = BSR(minIndex + maxIndex, 1);
  32.         if (offset >= pLines[index].lineStart) 
  33.         {
  34.             if (offset < pLines[index + 1].lineStart) 
  35.             {
  36.                 break;
  37.             }
  38.             else
  39.             {
  40.                 minIndex = index + 1;
  41.             }
  42.         }
  43.         else
  44.         {
  45.             maxIndex = index;
  46.         }
  47.     }  //{ while }
  48.  
  49.     return index;
  50. }
  51.  
  52. long _WEPixelToLine(long vOffset, WEHandle hWE)
  53. {
  54.     //{ given a vertical pixel offset in local coordinates, }
  55.     //{ find the corresponding line index }
  56.  
  57.     WEPtr pWE;
  58.     LineArrayPtr pLines;
  59.     long minIndex, maxIndex, index;
  60.     
  61.     pWE = *hWE;
  62.  
  63.     //{ get a pointer to the line array }
  64.     pLines = *pWE->hLines;
  65.  
  66.     //{ do a fast binary search through the style run array }
  67.     minIndex = 0;
  68.     maxIndex = pWE->nLines;
  69.  
  70.     while (minIndex < maxIndex)
  71.     {
  72.         index = BSR(minIndex + maxIndex, 1);
  73.         if (vOffset >= pLines[index].lineOrigin) 
  74.         {
  75.             if (vOffset < pLines[index + 1].lineOrigin) 
  76.             {
  77.                 break;
  78.             }
  79.             else
  80.             {
  81.                 minIndex = index + 1;
  82.             }
  83.         }
  84.         else
  85.         {
  86.             maxIndex = index;
  87.         }
  88.     }
  89.     
  90.     return index;
  91. }
  92.  
  93. long _WEOffsetToRun (long offset, WEHandle hWE)
  94. {
  95.     WEPtr pWE;
  96.     RunArrayPtr pRuns;
  97.     long minIndex, maxIndex, index;
  98.  
  99.     pWE = *hWE;
  100.  
  101.     //{ get a pointer to the style run array }
  102.     pRuns = *pWE->hRuns;
  103.  
  104.     //{ do a fast binary search through the style run array }
  105.     minIndex = 0;
  106.     maxIndex = pWE->nRuns;
  107.  
  108.     while (minIndex < maxIndex)
  109.     {
  110.         index = BSR(minIndex + maxIndex, 1);
  111.         if (offset >= pRuns[index].runStart)
  112.         { 
  113.             if (offset < pRuns[index + 1].runStart) 
  114.             {
  115.                 break;
  116.             }
  117.             else
  118.             {
  119.                 minIndex = index + 1;
  120.             }
  121.         }
  122.         else
  123.         {
  124.             maxIndex = index;
  125.         }
  126.     } //{ while }
  127.     return index;
  128. }
  129.  
  130. void _WEGetIndStyle(long runIndex, WERunInfo *info, WEHandle hWE)
  131. {
  132.     WEPtr pWE;
  133.     RunArrayPeek pTheRun;
  134.  
  135.     pWE = *hWE;
  136.  
  137.     //{ get a pointer to the specified run array element }
  138.     pTheRun = (RunArrayPeek)&(*pWE->hRuns)[runIndex];
  139.  
  140.     //{ fill in the runStart and runEnd fields from the style run array }
  141.     info->runStart = pTheRun->first.runStart;
  142.     info->runEnd = pTheRun->second.runStart;
  143.  
  144.     //{ copy the style information from the appropriate entry in the style table }
  145.     info->runAttrs = (*pWE->hStyles)[pTheRun->first.styleIndex].info;
  146. }
  147.  
  148. pascal void WEGetRunInfo(long offset, WERunInfo *info, WEHandle hWE)
  149. {
  150.     _WEGetIndStyle(_WEOffsetToRun(offset, hWE), info, hWE);
  151. }
  152.  
  153. void _WEContinuousStyleRange(long rangeStart, long rangeEnd, short *mode,
  154.         WETextStyle *ts, WEHandle hWE)
  155. {
  156.     //{ find out which style attributes are continous over the specified text range }
  157.     //{ on entry, the mode bitmap specifies which attributes are to be checked }
  158.     //{ on exit, the mode bitmap specifies the continuous attributes, also copied to ts }
  159.  
  160.     WEPtr pWE;
  161.     long bitmap;
  162.     long runIndex;
  163.     WERunInfo runInfo;
  164.     
  165.     pWE = *hWE;
  166.  
  167.     //{ get bitmap of style attributes to check (valid bits are kModeFont..kModeColor) }
  168.     bitmap = *mode & weDoAll;
  169.  
  170.     //{ get style info at the beginning of the specified range }
  171.     runIndex = _WEOffsetToRun(rangeStart, hWE);
  172.     _WEGetIndStyle(runIndex, &runInfo, hWE);
  173.  
  174.     //{ copy the specified fields to ts }
  175.     _WECopyStyle(&runInfo.runAttrs.runStyle, ts, 0, *mode | weDoReplaceFace);
  176.  
  177.     //{ loop through style runs across the current selection range }
  178.     //{ if we determine that all specified attributes are discontinuous, we exit prematurely }
  179.     do
  180.     {
  181.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  182.  
  183.         //{ determine which attributes have changed, if any }
  184.         if (BTST(bitmap, kModeFont))
  185.         { 
  186.             if (runInfo.runAttrs.runStyle.tsFont != ts->tsFont)
  187.             { 
  188.                     BCLR(bitmap, kModeFont);
  189.             }
  190.         }
  191.         if (BTST(bitmap, kModeFace))
  192.         { 
  193.             if (runInfo.runAttrs.runStyle.tsFace != ts->tsFace)
  194.             { 
  195.                 ts->tsFace = ts->tsFace & runInfo.runAttrs.runStyle.tsFace;
  196.                 if (ts->tsFace == 0)
  197.                 { 
  198.                     BCLR(bitmap, kModeFace);
  199.                 }
  200.             }
  201.         }
  202.         if (BTST(bitmap, kModeSize))
  203.         { 
  204.             if (runInfo.runAttrs.runStyle.tsSize != ts->tsSize) 
  205.             {
  206.                 BCLR(bitmap, kModeSize);
  207.             }
  208.         }
  209.         if (BTST(bitmap, kModeColor))
  210.         { 
  211.             if (!_WEBlockCmp((Ptr)&runInfo.runAttrs.runStyle.tsColor, (Ptr)&ts->tsColor, sizeof(RGBColor)))
  212.             { 
  213.                 BCLR(bitmap, kModeColor);
  214.             }
  215.         }
  216.  
  217.         runIndex = runIndex + 1;
  218.     } while ((bitmap != 0) && (runInfo.runEnd < rangeEnd));
  219.  
  220.     *mode = bitmap;
  221. }
  222.  
  223. void _WESynchNullStyle(WEHandle hWE)
  224. {
  225.     //{ This routine fills the nullStyle field of the WE record with valid information }
  226.     //{ and makes sure that the null style font belongs to the keyboard script. }
  227.  
  228.     WEPtr pWE;
  229.     long runIndex;
  230.     ScriptCode keyboardScript;
  231.     short fontID;
  232.     WERunInfo runInfo;
  233.     
  234.     pWE = *hWE;
  235.  
  236.     //{ find the run index of the style run preceding the insertion point }
  237.     runIndex = _WEOffsetToRun(pWE->selStart - 1, hWE);
  238.  
  239.     //{ if the nullStyle record is marked as invalid, fill it with the style attributes }
  240.     //{ associated with the character preceding the insertion point, and mark it as valid }
  241.     if (!BTST(pWE->flags, weFUseNullStyle)) 
  242.     {
  243.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  244.         pWE->nullStyle = runInfo.runAttrs;
  245.         BSET(pWE->flags, weFUseNullStyle);
  246.     }
  247.  
  248.     //{ if only the Roman script is installed, we're finished }
  249.     if (!BTST(pWE->flags, weFNonRoman)) 
  250.     {
  251.         return;
  252.     }
  253.  
  254.     //{ *** FONT / KEYBOARD SYNCHRONIZATION *** }
  255.     //{ get the keyboard script }
  256.     keyboardScript = GetEnvirons(smKeyScript);
  257.  
  258.     //{ find out what font will be used for the next character typed }
  259.     fontID = pWE->nullStyle.runStyle.tsFont;
  260.  
  261.     //{ do nothing if the font script is the same as the keyboard script }
  262.     if (Font2Script(fontID) == keyboardScript) return; 
  263.  
  264.     //{ scan style runs starting from the insertion point backwards,}
  265.     //{ looking for the first font belonging to the keyboard script }
  266.     do
  267.     {
  268.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  269.         fontID = runInfo.runAttrs.runStyle.tsFont;
  270.         if (Font2Script(fontID) == keyboardScript) break;
  271.         runIndex = runIndex - 1;
  272.     } while (runIndex>=0);
  273.     
  274.     //{ if no font was ever used for the keyboard script, default to the }
  275.     //{ application font for the script }
  276.     if (runIndex < 0) 
  277.     {
  278.         fontID = GetScript(keyboardScript, smScriptAppFond);
  279.     }
  280.     
  281.     //{ change the font in the null style record }
  282.     pWE->nullStyle.runStyle.tsFont = fontID;
  283. }
  284.  
  285. pascal Boolean WEContinuousStyle (short *mode, WETextStyle *ts, WEHandle hWE)
  286. {
  287.     //{ find out which style attributes are continous over the selection range }
  288.     //{ on entry, the mode bitmap specifies which attributes are to be checked }
  289.     //{ on exit, the mode bitmap specifies the continuous attributes, also copied to ts }
  290.     //{ return TRUE if all specified attributes are continuous }
  291.  
  292.     WEPtr pWE;
  293.     short oldMode;
  294.     Boolean continuousStyle;
  295.     
  296.     pWE = *hWE;
  297.  
  298.     //{ two rather different paths are taken depending on whether }
  299.     //{ the selection range is empty or not }
  300.     if (pWE->selStart == pWE->selEnd) 
  301.     {
  302.         //{ if the selection range is empty, always return TRUE and set ts }
  303.         //{ from the nullStyle record, after having validated it }
  304.         continuousStyle = true;
  305.         _WESynchNullStyle(hWE);
  306.         _WECopyStyle(&pWE->nullStyle.runStyle, ts, 0, *mode | weDoReplaceFace);
  307.     }
  308.     else
  309.     {
  310.         //{ otherwise get the continuous style attributes over the selection range }
  311.         oldMode = *mode;
  312.         _WEContinuousStyleRange(pWE->selStart, pWE->selEnd, mode, ts, hWE);
  313.  
  314.         //{ return TRUE if mode hasn't changed }
  315.         continuousStyle = (oldMode == *mode);
  316.     }
  317.     return continuousStyle;
  318. }
  319.  
  320. void _WESegmentLoop(long firstLine, long lastLine, SegmentLoopProcPtr callback, void *callbackData,
  321.         WEHandle hWE)
  322. {
  323.     //{ For each style segment on every line in the specified range, set up }
  324.     //{ text attributes in the port and call the callback. }
  325.     //{ the WE record must be already locked }
  326.  
  327.     WEPtr pWE;
  328.     LineArrayPtr pLines;
  329.     long pText;
  330.     long lineIndex;
  331.     long runIndex, previousRunIndex;
  332.     long lineStart, lineEnd, segmentStart, segmentEnd;
  333.     JustStyleCode styleRunPosition;
  334.     WERunInfo runInfo;
  335.     Boolean saveLineLock;
  336.     Boolean saveTextLock;
  337.     QDEnvironment saveEnvironment;
  338.     
  339.     pWE = *hWE;
  340.  
  341.     //{ save the QuickDraw environment }
  342.     _WESaveQDEnvironment(pWE->port, BTST(pWE->flags, weFHasColorQD), &saveEnvironment);
  343.  
  344.     //{ make sure firstLine and lastLine are within the allowed range }
  345.     lineIndex = pWE->nLines - 1;
  346.     firstLine = _WEPinInRange(firstLine, 0, lineIndex);
  347.     lastLine = _WEPinInRange(lastLine, 0, lineIndex);
  348.  
  349.     //{ lock the line array }
  350.     saveLineLock = _WESetHandleLock((Handle)pWE->hLines, true);
  351.     pLines = *pWE->hLines;
  352.  
  353.     //{ lock the text }
  354.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  355.     pText = (long)(*pWE->hText);
  356.  
  357.     //{ find the style run index corresponding to the beginning of the first line }
  358.     runIndex = _WEOffsetToRun(pLines[firstLine].lineStart, hWE);
  359.     previousRunIndex = -1;
  360.  
  361.     //{ loop thru the specified lines }
  362.     for(lineIndex = firstLine; lineIndex<=lastLine; lineIndex++)
  363.     {
  364.         //{ get line start and line end }
  365.         lineStart = pLines[lineIndex].lineStart;
  366.         lineEnd = pLines[lineIndex + 1].lineStart;
  367.  
  368.         //{ loop thru each style run on this line }
  369.         do
  370.         {
  371.             //{ get style run information for the current style run }
  372.             _WEGetIndStyle(runIndex, &runInfo, hWE);
  373.  
  374.             if (previousRunIndex != runIndex) 
  375.             {
  376.                 //{ set new text attributes }
  377.                 TextFont(runInfo.runAttrs.runStyle.tsFont);
  378.                 TextFace(runInfo.runAttrs.runStyle.tsFace);
  379.                 TextSize(runInfo.runAttrs.runStyle.tsSize);
  380.  
  381.                 //{ remember previous run index }
  382.                 previousRunIndex = runIndex;
  383.             }
  384.  
  385.             //{ determine the relative position of this style run on the line }
  386.             styleRunPosition = 0;                                        //{ onlyStyleRun }
  387.  
  388.             if (runInfo.runStart <= lineStart)
  389.             { 
  390.                 segmentStart = lineStart;
  391.             }
  392.             else
  393.             {
  394.                 styleRunPosition = styleRunPosition + 2;    //{ rightStyleRun or middleStyleRun }
  395.                 segmentStart = runInfo.runStart;
  396.             }
  397.  
  398.             if (runInfo.runEnd >= lineEnd)
  399.             {
  400.                         segmentEnd = lineEnd;
  401.             }
  402.             else
  403.             {
  404.                 styleRunPosition = styleRunPosition + 1;    //{ leftStyleRun or middleStyleRun }
  405.                 segmentEnd = runInfo.runEnd;
  406.             }
  407.  
  408.             //{ do the callback }
  409.             if ((callback)(&pLines[lineIndex], &runInfo.runAttrs, (Ptr)(pText + segmentStart),
  410.                     segmentStart, segmentEnd - segmentStart, styleRunPosition, callbackData))
  411.             {
  412.                 break;
  413.             }
  414.  
  415.             //{ advance style run index, unless this style run goes on to the next line }
  416.             if (runInfo.runEnd <= lineEnd)
  417.             { 
  418.                 runIndex = runIndex + 1;
  419.             }
  420.         } while (runInfo.runEnd < lineEnd);
  421.     }  //{ for }
  422.  
  423.     //{ unlock the text }
  424.     _WESetHandleLock((Handle)pWE->hText, saveTextLock);
  425.  
  426.     //{ unlock the line array }
  427.     _WESetHandleLock((Handle)pWE->hLines, saveLineLock);
  428.  
  429.     //{ restore the QuickDraw environment }
  430.     _WERestoreQDEnvironment(&saveEnvironment);
  431. }
  432.  
  433. void _WEDrawTSMHilite(Rect *segmentRect, short tsFlags)
  434. {
  435.     //QDPtr qd;
  436.     long flags;
  437.     short underlineHeight;
  438.     RGBColor background, foreground, saveForeground;
  439.     Boolean isColorPort;
  440.     Boolean usingTrueGray;
  441.  
  442.     flags = tsFlags;
  443.     //qd = GetQDGlobals();
  444.     isColorPort = (((CGrafPtr)(qd.thePort))->portVersion < 0);
  445.     usingTrueGray = false;
  446.  
  447.     //{ by default, the pen pattern is solid }
  448.     PenPat(&qd.black);
  449.  
  450.     //{ if we're drawing in color, set the foreground color }
  451.     if (isColorPort) 
  452.     {
  453.         //{ save foreground color }
  454.         GetForeColor(&saveForeground);
  455.  
  456.         //{ by default, the foreground color is black }
  457.         foreground.red = 0;
  458.         foreground.green = 0;
  459.         foreground.blue = 0;
  460.  
  461.         //{ if we're underlining raw (unconverted) text, see if a "true gray" is available }
  462.         if (!BTST(flags, tsTSMConverted)) 
  463.         {
  464.             GetBackColor(&background);
  465.             usingTrueGray = GetGray(GetGDevice(), &background, &foreground);
  466.         } //{ if raw text }
  467.  
  468.         //{ set the foreground color }
  469.         RGBForeColor(&foreground);
  470.     } //{ if color graf port }
  471.  
  472.     //{ if we're underlining raw (unconverted) text and no true gray is available, }
  473.     //{ simulate gray with a 50% pattern }
  474.     if (!BTST(flags, tsTSMConverted)) 
  475.     {
  476.         if (usingTrueGray == false)
  477.         { 
  478.                 PenPat(&qd.gray);
  479.         }
  480.     }
  481.     //{ use a 2-pixel tall underline if text is "selected", else use a 1-pixel tall underline }
  482.     if( BTST(flags, tsTSMSelected)) 
  483.     {
  484.         underlineHeight = 2;
  485.     }
  486.     else
  487.     {
  488.         underlineHeight = 1;
  489.     }
  490.     
  491.     //{ segmentRect becomes the rectangle to paint }
  492.     InsetRect(segmentRect, 1, 0);
  493.     segmentRect->top = segmentRect->bottom - underlineHeight;
  494.  
  495.     //{ draw the underline }
  496.     PaintRect(segmentRect);
  497.  
  498.     //{ restore the foreground color }
  499.     if (isColorPort) 
  500.     {
  501.         RGBForeColor(&saveForeground);
  502.     }
  503. }
  504.  
  505. Boolean SLDraw (LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  506.         long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  507.         void *callbackData);
  508.  
  509. // typedef BitMap *BitMapPtr; removed in 1.0
  510.  
  511. Boolean SLDraw (LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
  512.         long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
  513.         void *callbackData)
  514. {
  515.     struct SLDrawData *p = (struct SLDrawData *) callbackData;
  516.     WEPtr pWE = p->pWE;
  517.     Fixed slop;
  518.     Rect segmentRect;
  519.     RGBColor theColor;
  520.     Boolean retval;
  521.     Rect drawRect;                    //{ visible portion of the line rectangle }
  522.     PixMapHandle offscreenPixels;
  523.     GrafPtr screenPort;
  524.     GDHandle screenDevice;
  525.     Rect lineRect;                    //{ rectangle enclosing the current line }
  526.     
  527.     retval = false;                            //{ keep looping }
  528.  
  529.     //{ is this the first segment on this line? }
  530.     if (styleRunPosition <= smLeftStyleRun) 
  531.     {
  532.         //{ calculate the line rectangle (the rectangle which completely encloses the current line) }
  533.         lineRect.left = pWE->destRect.left;
  534.         lineRect.right = pWE->destRect.right;
  535.         lineRect.top = pWE->destRect.top + pLine->lineOrigin;
  536.         lineRect.bottom = pWE->destRect.top + ((LinePeek)pLine)->second.lineOrigin;
  537.  
  538.         //{ calculate the visible portion of this rectangle }
  539.         //{ we do this by intersecting the line rectangle with the view rectangle }
  540.         drawRect = (*pWE->viewRgn)->rgnBBox;
  541.         if (SectRect(&lineRect, &drawRect, &drawRect))
  542.         { 
  543.             ;
  544.         }
  545.         if (p->usingOffscreen) 
  546.         {
  547.             //{ calculate the boundary rectangle for the offscreen buffer }
  548.             //{ this is simply drawRect converted to global coordinates }
  549.             p->bounds = drawRect;
  550.             LocalToGlobal((Point *)&p->bounds.top);
  551.             LocalToGlobal((Point *)&p->bounds.bottom);
  552.  
  553.             //{ update the offscreen graphics world for the new bounds (this could fail) }
  554.             p->drawingOffscreen = false;
  555.             if (UpdateGWorld((GWorldPtr *)(&pWE->offscreenPort), 0, &p->bounds, (CTabHandle)nil,
  556.                 (GDHandle)nil, (GWorldFlags)0) >= 0) 
  557.             {
  558.                 //{ NOTE: when running on a 68000 machine with the original QuickDraw, }
  559.                 //{ a GWorld is just an extended GrafPort, and GetGWorldPixMap actually }
  560.                 //{ returns a handle to a _copy_ of the GrafPort portBits (a BitMap, not a PixMap). }
  561.                 //{ An important side-effect of this is that when we call SetOrigin, }
  562.                 //{ only the original portBits is offset, not the copy. }
  563.                 //{ get the pixel map associated with the offscreen graphics world }
  564.                 offscreenPixels = GetGWorldPixMap((GWorldPtr)(pWE->offscreenPort));
  565.  
  566.                 //{ lock it down }
  567.                 if (LockPixels(offscreenPixels)) 
  568.                 {
  569.                     //{ offscreen pixel buffer allocation was successful }
  570.                     p->drawingOffscreen = true;
  571.  
  572.                     //{ switch graphics world }
  573.                     GetGWorld((GWorldPtr *)(&screenPort), &screenDevice);
  574.                     SetGWorld((GWorldPtr)(pWE->offscreenPort), nil);
  575.  
  576.                     //{ synchronize the coordinate system of the offscreen port with that of the screen port }
  577.                     SetOrigin(drawRect.left, drawRect.top);
  578.  
  579.                     //{ reset the offscreen clip region }
  580.                     ClipRect(&drawRect);
  581.                 }
  582.             } //{ if pixel buffer allocation was successful }
  583.         } //{ if usingOffscreen }
  584.  
  585.         //{ if doErase is TRUE, erase the drawable area before drawing text }
  586.         if (p->doErase) 
  587.         {
  588.             EraseRect(&drawRect);
  589.         }
  590.         //{ position the pen }
  591.         MoveTo(lineRect.left + _WECalcPenIndent(pLine->lineSlop, pWE->alignment),
  592.             lineRect.top + pLine->lineAscent);
  593.     } //{ if first segment on line }
  594.  
  595.     //{ if drawingOffscreen, switch thePort to the offscreen port }
  596.     //{ and synchronize text attributes }
  597.     if (p->drawingOffscreen) 
  598.     {
  599.         SetPort(pWE->offscreenPort);
  600.         TextFont(pAttrs->runStyle.tsFont);
  601.         TextFace(pAttrs->runStyle.tsFace);
  602.         TextSize(pAttrs->runStyle.tsSize);
  603.     } //{ if drawingOffscreen }
  604.  
  605.     //{ get horizontal coordinate of the pen before drawing the segment }
  606.     GetPen((Point *)&segmentRect.top);
  607.  
  608.     //{ set the foreground color }
  609.     if (p->usingColor)
  610.     { 
  611.         RGBForeColor(&pAttrs->runStyle.tsColor);
  612.     }
  613.     slop = 0;
  614.  
  615.     //{ calculate the "slop" (extra space) for this text segment (justified text only) }
  616.     if (pWE->alignment == weJustify) 
  617.     {
  618.         //{ if this is the last segment on the line, strip trailing spaces }
  619.         if (!(styleRunPosition & 1))
  620.         {
  621.             segmentLength = VisibleLength(pSegment, segmentLength);
  622.         }
  623.         //{ calculate how much extra space is to be applied to this text segment }
  624.         slop = FixMul(NPortionText(pSegment, segmentLength, styleRunPosition, 
  625.             *(Point *)(&kOneToOneScaling), *(Point *)(&kOneToOneScaling)), pLine->lineJustAmount);
  626.  
  627.     } //{ if alignment = weJustify }
  628.  
  629.     //{ draw the segment }
  630.     NDrawJust(pSegment, segmentLength, slop, styleRunPosition, *(Point *)(&kOneToOneScaling),
  631.         *(Point *)(&kOneToOneScaling));
  632.  
  633.     //{ get horizontal coordinate of the pen after drawing the segment }
  634.     GetPen((Point *)&segmentRect.bottom);
  635.     segmentRect.bottom = lineRect.bottom;
  636.  
  637.     //{ if this segment is in the TSM area, underline it in the appropriate way }
  638.     if (BTST(pAttrs->runStyle.tsFlags, tsTSMHilite)) 
  639.     {
  640.         _WEDrawTSMHilite(&segmentRect, pAttrs->runStyle.tsFlags);
  641.     }
  642.     if (p->drawingOffscreen) 
  643.     {
  644.         if (!(styleRunPosition & 1)) 
  645.         {
  646.             //{ after drawing offscreen the last segment, }
  647.             //{ prepare to copy the offscreen buffer to video RAM }
  648.  
  649.             //{ first set the graphics world to the screen port }
  650.             SetGWorld((GWorldPtr)(screenPort), screenDevice);
  651.  
  652.             //{ before calling CopyBits, set the foreground color to black to avoid colorization (color only) }
  653.             if (p->usingColor) 
  654.             {
  655.                 theColor.red = 0;
  656.                 theColor.green = 0;
  657.                 theColor.blue = 0;
  658.                 RGBForeColor(&theColor);
  659.             }
  660.             
  661.             //{ copy the offscreen image of the [visible portion of the] line to the screen }
  662.             CopyBits(&pWE->offscreenPort->portBits, &screenPort->portBits, &drawRect,
  663.                     &drawRect, srcCopy, (RgnHandle)nil);
  664.  
  665.             //{ restore the original offscreen coordinate system and unlock the pixel image }
  666.             SetPort(pWE->offscreenPort);
  667.             SetOrigin(0, 0);
  668.             UnlockPixels(offscreenPixels);
  669.  
  670.         } //{ if last segment }
  671.  
  672.         //{ restore the screen port for _WESegmentLoop }
  673.         SetPort(screenPort);
  674.     } //{ if drawingOffscreen }
  675.     return retval;
  676. }
  677.  
  678. void _WEDrawLines (long firstLine, long lastLine, Boolean doErase, WEHandle hWE)
  679. {
  680.     //{ draw the specified range of lines }
  681.     //{ we can safely assume that the WE record is already locked }
  682.     //{ and the port is already set the pWE->port }
  683.  
  684.     WEPtr pWE;
  685.     Rect bounds;                    //{ bounds of the offscreen buffer, in global coordinates }
  686.     Boolean usingColor;                //{ TRUE if we're drawing in color }
  687.     Boolean usingOffscreen;            //{ TRUE if we're using an offscreen port }
  688.     Boolean drawingOffscreen;        //{ TRUE if actually drawing to an offscreen buffer }
  689.     struct SLDrawData callbackData;
  690.  
  691.  
  692.     pWE = *hWE;
  693.     usingOffscreen = false;
  694.     drawingOffscreen = false;
  695.  
  696.     //{ do nothing if our graphics port is not visible }
  697.     if (EmptyRgn(pWE->port->visRgn))
  698.     {
  699.         return;
  700.     }
  701.  
  702.     //{ If doErase is TRUE, we're drawing over old text, so we must erase each line }
  703.     //{ before redrawing it.  But if the weFDrawOffscreen feature is enabled, we draw }
  704.     //{ the entire line offscreen and  we copy the image right over the old line, }
  705.     //{ without erasing it, thus achieving a very smooth drawing effect. }
  706.  
  707.     if ((doErase) && BTST(pWE->flags, weFDrawOffscreen)) 
  708.     {
  709.         //{ has an offscreen world already been allocated? }
  710.         if (pWE->offscreenPort == nil) 
  711.         {
  712.             //{ nope,  create one; its bounds are set initially to an arbitrary rectangle }
  713.             SetRect(&bounds, 0, 0, 1, 1);
  714.             NewGWorld((GWorldPtr *)(/*&*/pWE->offscreenPort), 0, &bounds, nil, nil,
  715.                 pixPurge + noNewDevice + useTempMem);
  716.         }
  717.         usingOffscreen = (pWE->offscreenPort != nil);
  718.     }
  719.  
  720.     usingColor = BTST(pWE->flags, weFHasColorQD);
  721.     callbackData.pWE = pWE;
  722.     callbackData.bounds = bounds;
  723.     callbackData.usingColor = usingColor;
  724.     callbackData.usingOffscreen = usingOffscreen;
  725.     callbackData.drawingOffscreen = drawingOffscreen;
  726.     callbackData.doErase = doErase;
  727.     _WESegmentLoop(firstLine, lastLine, SLDraw, (void *) &callbackData, hWE);
  728. }
  729.  
  730. OSErr _WEAllocate(Size blockSize, short allocFlags, Handle *h)
  731. {
  732.     //{ Allocate a new relocatable block. }
  733.     //{ AllocFlags may specify whether the block should be cleared and whether }
  734.     //{ temporary memory should be used. }
  735.  
  736.     Handle theHandle;
  737.     OSErr retval;
  738.     
  739.     theHandle = nil;
  740.  
  741.     //{ if kAllocTemp is specified, try tapping temporary memory }
  742.     if ((allocFlags & kAllocTemp) != 0) 
  743.     {
  744.         theHandle = TempNewHandle(blockSize, &retval);
  745.     }
  746.     //{ if kAllocTemp isn't specified, or TempNewHandle failed, try with current heap }
  747.     if (theHandle == nil) 
  748.     {
  749.         theHandle = NewHandle(blockSize);
  750.         retval = MemError();
  751.     }
  752.     
  753.     //{ if kAllocClear is specified, zero the block }
  754.     if ((allocFlags & kAllocClear) != 0) 
  755.     {
  756.         if (theHandle != nil) 
  757.         {
  758.             _WEBlockClr(*theHandle, blockSize);
  759.         }
  760.     }
  761.  
  762.     //{ return handle through VAR parameter }
  763.     *h = theHandle;
  764.     
  765.     return retval;
  766. }
  767.  
  768. short _WECalcPenIndent(short slop, short alignment)
  769. {
  770.     short retval;
  771.  
  772.     //{ if alignment is weFlushDefault, use the system global SysDirection }
  773.     if (alignment == weFlushDefault) 
  774.     {
  775.         if (GetSysJust() == 0)
  776.         { 
  777.             alignment = weFlushLeft;
  778.         }
  779.         else
  780.         {
  781.             alignment = weFlushRight;
  782.         }
  783.     }
  784.     if (alignment == weFlushRight) 
  785.     {
  786.         retval = slop;                                //{ right aligned }
  787.     }
  788.     else if (alignment == weCenter) 
  789.     {
  790.         retval = slop / 2;                        //{ centered }
  791.     }
  792.     else
  793.     {
  794.         retval = 0;                                    //{ left aligned or justified }
  795.     }
  796.     return retval;
  797. }
  798.  
  799. void _WESaveQDEnvironment(GrafPtr port, Boolean saveColor, QDEnvironment *theEnvironment)
  800. {
  801.     GetPort(&theEnvironment->envPort);
  802.     SetPort(port);
  803.     GetPenState(&theEnvironment->envPen);
  804.     PenNormal();
  805.     theEnvironment->envStyle.tsFont = port->txFont;
  806.     theEnvironment->envStyle.tsFace = ((GrafPtr1)port)->txFace;
  807.     theEnvironment->envStyle.tsFlags = saveColor;        //{ remember if color was saved }
  808.     theEnvironment->envStyle.tsSize = port->txSize;
  809.     if (saveColor) 
  810.     {
  811.         GetForeColor(&theEnvironment->envStyle.tsColor);
  812.     }
  813.     theEnvironment->envMode = port->txMode;
  814.     TextMode(srcOr);
  815. }
  816.  
  817. void _WERestoreQDEnvironment(QDEnvironment *theEnvironment)
  818. {
  819.     SetPenState(&theEnvironment->envPen);
  820.     TextFont(theEnvironment->envStyle.tsFont);
  821.     TextFace(theEnvironment->envStyle.tsFace);
  822.     TextSize(theEnvironment->envStyle.tsSize);
  823.     TextMode(theEnvironment->envMode);
  824.     if (theEnvironment->envStyle.tsFlags) 
  825.     {
  826.         RGBForeColor(&theEnvironment->envStyle.tsColor);
  827.     }
  828.     SetPort(theEnvironment->envPort);
  829. }
  830.  
  831. void _WEFillFontInfo (GrafPtr port, WERunAttributes *targetStyle)
  832. {
  833.     //{ given a WERunAttributes record, fill in the runHeight, runAscent fields etc. }
  834.     FontInfo fInfo;
  835.     QDEnvironment saveEnvironment;
  836.  
  837.     _WESaveQDEnvironment(port, false, &saveEnvironment);
  838.  
  839.     //{ we don't want a zero font size; although QuickDraw accepts zero to mean }
  840.     //{ the default font size, it can cause trouble to us when we do calculations }
  841.     if (targetStyle->runStyle.tsSize == 0) 
  842.     {
  843.         targetStyle->runStyle.tsSize = 12;
  844.     }
  845.     
  846.     //{ set the text attributes }
  847.     TextFont(targetStyle->runStyle.tsFont);
  848.     TextSize(targetStyle->runStyle.tsSize);
  849.     TextFace(targetStyle->runStyle.tsFace);
  850.     GetFontInfo(&fInfo);
  851.     targetStyle->runHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  852.     targetStyle->runAscent = fInfo.ascent;
  853.     _WERestoreQDEnvironment(&saveEnvironment);
  854. }
  855.  
  856. void _WECopyStyle (WETextStyle *sourceStyle, WETextStyle *targetStyle, short offStyles,
  857.         short mode)
  858. {
  859.     //{ Copy some or all of the attributes composing sourceStyle to targetStyle. }
  860.     //{ The mode parameter determines which attributes are to be copied and how. }
  861.     //{ If mode contains weDoToggleFace,  offStyles indicates which }
  862.     //{ QuickDraw styles are to be turned off. }
  863.  
  864.     long longMode;
  865.     long longSize;
  866.     long sourceFace, targetFace;
  867.  
  868.     longMode = mode;    //{ this allows the compiler to generate tighter code }
  869.  
  870.     //{ if the kModeFont bit is set, copy the font family number }
  871.     if (BTST(longMode, kModeFont))
  872.     { 
  873.             targetStyle->tsFont = sourceStyle->tsFont;
  874.     }
  875.     
  876.     //{ if the kModeSize or the kModeAddSize bit is set, alter the font size }
  877.     if ((longMode & (weDoSize + weDoAddSize)) != 0) 
  878.     {
  879.         longSize = sourceStyle->tsSize;
  880.  
  881.         //{ if kModeAddSize is set, the source size is added to the target size, }
  882.         //{ otherwise the source size replaces the target size outright }
  883.         if (BTST(longMode, kModeAddSize)) 
  884.         {
  885.             longSize = longSize + targetStyle->tsSize;
  886.         }
  887.         //{ range-check the resulting size }
  888.         longSize = _WEPinInRange(longSize, kMinFontSize, kMaxFontSize);
  889.         targetStyle->tsSize = longSize;
  890.     } //{ if alter size }
  891.  
  892.     //{ if kModeFace is set, copy the QuickDraw styles (tsFace field); }
  893.     //{ the (rather complex) rules for copying the styles are explained below in detail }
  894.     if (BTST(longMode, kModeFace)) 
  895.     {
  896.         sourceFace = sourceStyle->tsFace;
  897.         targetFace = targetStyle->tsFace;
  898.     
  899.         //{ sourceFace replaces targetFace outright if one or both of these conditions hold: }
  900.         //{ 1. sourceFace is zero (= empty set = plain text) }
  901.         //{ 2. the kModeReplaceFace bit is set }
  902.     
  903.         if ((sourceFace == 0) || BTST(longMode, kModeReplaceFace)) 
  904.         {
  905.             targetFace = sourceFace;
  906.         }
  907.         else
  908.         {
  909.             //{ Otherwise sourceFace is interpreted as a bitmap indicating }
  910.             //{ which styles are to be altered -- all other styles are left intact. }
  911.             //{ What exactly happens to the styles indicated in sourceFace }
  912.             //{ depends on whether the kModeToggleFace bit is set or clear. }
  913.     
  914.             //{ if kModeToggleFace is set, turn a style off if it's set in offStyles, else turn it on }
  915.             if (BTST(longMode, kModeToggleFace)) 
  916.             {
  917.                 targetFace = (sourceFace ^ offStyles) | (targetFace & (~sourceFace));
  918.             }
  919.             else
  920.             {
  921.                 //{ if kModeToggleFace is clear, turn on the styles specified in sourceStyle }
  922.                 targetFace = targetFace | sourceFace;
  923.             }
  924.             //{ the condense and extend attributes are mutually exclusive: if one is set }
  925.             //{ in sourceFace, remove it from targetFace }
  926.             if (BTST(sourceFace, tsCondense))
  927.             { 
  928.                 BCLR(targetFace, tsExtend);
  929.             }
  930.             else if (BTST(sourceFace, tsExtend)) 
  931.             {
  932.                 BCLR(targetFace, tsCondense);
  933.             }
  934.         }
  935.         targetStyle->tsFace = targetFace;
  936.     }  //{ if alter face }
  937.  
  938.     //{ if kModeColor is set, change target color }
  939.     if (BTST(longMode, kModeColor)) 
  940.     {
  941.         targetStyle->tsColor = sourceStyle->tsColor;
  942.     }
  943.     
  944.     //{ always clear targetStyle.tsFlags by default }
  945.     targetStyle->tsFlags = 0;
  946.  
  947.     //{ if kModeFlags is set, copy the tsFlags field }
  948.     if (BTST(longMode, kModeFlags))
  949.     { 
  950.         targetStyle->tsFlags = sourceStyle->tsFlags;
  951.     }
  952. }
  953.  
  954. Boolean _WEOffsetInRange(long offset, char edge, long rangeStart, long rangeEnd)
  955. {
  956.     //{ return TRUE if the position specified by the pair < offset, edge > }
  957.     //{ is within the specified range }
  958.  
  959.     //{ if edge is kTrailingEdge, offset really refers to the preceding character }
  960.     if (edge == kTrailingEdge) 
  961.     {
  962.         if (offset > 0) 
  963.         {
  964.             offset = offset - 1;
  965.         }
  966.     }
  967.     //{ return TRUE iff offset is within the specified range }
  968.     return ((offset >= rangeStart) && (offset < rangeEnd));
  969. }
  970.